package com.grape.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 *  类描述： 由于Java的简单类型不能够精确的对浮点数进行运算，这个工具类提供精确的浮点数运算，包括加减乘除和四舍五入。
 *
 * @author  putao
 * @email   2628279194@qq.com
 */
public class BigDecimalUtil {

    private final static List<Integer> LENGTH_LIST = new ArrayList<>();

    public static final BigDecimal ONE_HUNDRED = new BigDecimal("100");

    static {
        LENGTH_LIST.add(8);
        LENGTH_LIST.add(9);
        LENGTH_LIST.add(10);
    }

    /**
     * 默认除法运算精度
     */
    private static final int DEF_DIV_SCALE = 10;

    /**
     * 这个类不能实例化
     */
    private BigDecimalUtil() {

    }

    /**
     * 提供精确的加法运算。
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static double add(double v1, double v2) {
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return b1.add(b2).doubleValue();
    }


    /**
     * 提供精确的加法运算。
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static int add(Integer v1, Integer v2) {
        if (v1 == null) {
            v1 = 0;
        }
        if (v2 == null) {
            v2 = 0;
        }
        BigDecimal b1 = new BigDecimal(Integer.toString(v1));
        BigDecimal b2 = new BigDecimal(Integer.toString(v2));
        return b1.add(b2).intValue();
    }


    /**
     * 判断值是否相等
     * @param v1    值1
     * @param v2    值2
     * @return      比较结果
     */
    public static boolean valueEqual(BigDecimal v1, BigDecimal v2) {
        if (v1 == null) {
            v1 = BigDecimal.ZERO;
        }
        if (v2 == null) {
            v2 = BigDecimal.ZERO;
        }
        return v1.compareTo(v2) == 0;
    }

    /**
     * 判断是否为空
     * @param v1    被加数
     * @param v2    加数
     * @return      两个参数的和
     */
    public static BigDecimal add(BigDecimal v1, BigDecimal v2) {
        if (v1 == null) {
            v1 = BigDecimal.ZERO;
        }
        if (v2 == null) {
            v2 = BigDecimal.ZERO;
        }
        return v1.add(v2);
    }

    /**
     * 获得绝对值
     * @param bigDecimal    参数值
     * @return              参数值的绝对值
     */
    public static BigDecimal abs(BigDecimal bigDecimal) {
        if (bigDecimal == null) {
            bigDecimal = BigDecimal.ZERO;
        }
        if (!biggerThenZero(bigDecimal)) {
            bigDecimal = mul(bigDecimal, new BigDecimal(-1));
        }
        return bigDecimal;
    }

    /**
     * 判断是否为空
     * @param values    可变参数值
     * @return          两个参数的和
     */
    public static BigDecimal add(BigDecimal... values) {
        BigDecimal total = BigDecimal.ZERO;
        for (BigDecimal temp : values) {
            if (temp == null) {
                temp = BigDecimal.ZERO;
            }
            total = total.add(temp);
        }
        return total;
    }

    /**
     * 判断是否为空
     * @param v1    被减数
     * @param v2    减数
     * @return      两个参数的差
     */
    public static BigDecimal sub(BigDecimal v1, BigDecimal v2) {
        if (v1 == null) {
            v1 = BigDecimal.ZERO;
        }
        if (v2 == null) {
            v2 = BigDecimal.ZERO;
        }
        return v1.subtract(v2);
    }

    /**
     * 提供非负数的减法
     * @param v1    被减数
     * @param v2    减数
     * @return      两个参数的差
     */
    public static BigDecimal subNonnegative(BigDecimal v1, BigDecimal v2) {
        if (v1 == null) {
            v1 = BigDecimal.ZERO;
        }
        if (v2 == null) {
            v2 = BigDecimal.ZERO;
        }
        BigDecimal result = v1.subtract(v2);
        return result.compareTo(BigDecimal.ZERO) > 0 ? result : BigDecimal.ZERO;
    }

    /**
     * 除法
     * @param v1:   被除数
     * @param v2:   除数
     * @return      相除结果
     */
    public static BigDecimal div(BigDecimal v1, BigDecimal v2) {
        if (v1 == null) {
            v1 = BigDecimal.ZERO;
        }
        if (v2 == null) {
            v2 = BigDecimal.ZERO;
        }
        if (v1.equals(BigDecimal.ZERO)) {
            return BigDecimal.ZERO;
        }
        return div(v1, v2, DEF_DIV_SCALE);
    }

    /**
     * 提供精确的减法运算。
     * @param v1    被减数
     * @param v2    减数
     * @return      两个参数的差
     */
    public static double sub(double v1, double v2) {
        BigDecimal b1 =  BigDecimal.valueOf(v1);
        BigDecimal b2 =  BigDecimal.valueOf(v2);
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     * @param v1    被乘数
     * @param v2    乘数
     * @return      两个参数的积
     */
    public static BigDecimal mul(BigDecimal v1, BigDecimal v2) {
        if (v1 == null || v2 == null) {
            return BigDecimal.ZERO;
        }
        return v1.multiply(v2);
    }

    /**
     * 提供精确的乘法运算。
     * @param v1    被乘数
     * @param v2    乘数
     * @return      两个参数的积
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 =  BigDecimal.valueOf(v1);
        BigDecimal b2 =  BigDecimal.valueOf(v2);
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 参数为空返回0反之本身
     * @param objValue  参数
     * @return          不为空返回自身反之0
     */
    public static BigDecimal convertBigDecimal(BigDecimal objValue) {
        if (objValue == null) {
            return BigDecimal.ZERO;
        }
        return objValue;
    }

    /**
     * 转换为String
     * @param bigDecimal    参数值
     * @return              参数值不为空返回本身字符串反之0
     */
    public static String convertString(BigDecimal bigDecimal) {
        if (bigDecimal == null) {
            return BigDecimal.ZERO.toString();
        }
        return bigDecimal.toString();
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到
     * 小数点以后10位，以后的数字四舍五入。
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return      两个参数的商
     */

    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 =  BigDecimal.valueOf(v1);
        BigDecimal b2 =  BigDecimal.valueOf(v2);
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return      两个参数的商
     */

    public static BigDecimal div(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.divide(v2, scale, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的小数位四舍五入处理。
     * @param v         需要四舍五入的数字
     * @param scale     小数点后保留几位
     * @return          四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b =  BigDecimal.valueOf(v);
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
    }


    /**
     * 是否等于零
     * @param money     参数
     * @return          是否等于零
     */
    public static boolean equalZero(BigDecimal money) {
        if (null == money) {
            return true;
        }
        return money.compareTo(BigDecimal.ZERO) == 0;
    }

    /**
     * 大于零
     * @param money     参数
     * @return          是否大于零
     */
    public static boolean biggerThenZero(BigDecimal money) {
        if (null == money) {
            return false;
        }
        return money.compareTo(BigDecimal.ZERO) > 0;
    }

    /**
     * 是否大于等于零
     * @param money     参数
     * @return          是否大于等于零
     */
    public static boolean biggerEqThenZero(BigDecimal money) {
        if (null == money) {
            return false;
        }
        return money.compareTo(BigDecimal.ZERO) >= 0;
    }

    /**
     * 是否小于零
     * @param money     小于零
     * @return          是否小于零
     */
    public static boolean smallerThenZero(BigDecimal money) {
        if (null == money) {
            return false;
        }
        return money.compareTo(BigDecimal.ZERO) < 0;
    }

    /**
     * 是否小于等于零
     * @param money     参数
     * @return          是否小于等于零
     */
    public static boolean notEqualZero(BigDecimal money) {
        if (null == money) {
            return false;
        }
        return money.compareTo(BigDecimal.ZERO) != 0;
    }

    /**
     * 将字符串转为bigDecimal对象
     * @param value     字符串
     * @return          字符串转为BigDecimal
     */
    public static BigDecimal valueOf(String value) {
        return StringUtils.isEmpty(value) ? BigDecimal.ZERO : new BigDecimal(value);
    }


    /**
     * 比较两个BigDecimal中的d1是否比d2大
     * @param d1    值1
     * @param d2    值2
     * @return      比较结果
     */
    public static boolean biggerThen(BigDecimal d1, BigDecimal d2) {
        if (d1 == null) {
            d1 = BigDecimal.ZERO;
        }
        if (d2 == null) {
            d2 = BigDecimal.ZERO;
        }
        return d1.compareTo(d2) > 0;
    }

    /**
     * 获取 8~10位的随机数
     * @return
     */
    public static synchronized int generateRandomNumber() {
        Integer digits = LENGTH_LIST.get(new Random().nextInt(3));
        int min = (int) Math.pow(10, digits - 1);
        int max = (int) Math.pow(10, digits) - 1;
        Random rand = new Random();
        return rand.nextInt(max - min + 1) + min;
    }

    public static Double getDoubleValue(BigDecimal bigDecimal) {
        return bigDecimal == null ? 0.00 : bigDecimal.setScale(2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 根据最大值生成从0到最大值的四个逐渐增大的值
     * @param max   最大值
     * @return  四个平均增加的值
     */
    public static List<Integer> getNumberList(int max) {
        List<Integer> values = new ArrayList<>();
        double step = max / 4.0;
        for (int i = 0; i < 4; i++) {
            values.add(Double.valueOf(i * step).intValue());
        }
        return values.stream().sorted(Comparator.comparingInt(Integer::intValue).reversed()).collect(Collectors.toList());
    }
}

